package org.jeecg.config.mybatis;

import java.util.ArrayList;
import java.util.List;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import org.jeecg.common.annotation.SkipTenant;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;

import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.shiro.SecurityUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * 单数据源配置（jeecg.datasource.open = false时生效）
 *
 * @Author zhoujf
 */
@Configuration
@MapperScan(value = {"org.jeecg.modules.**.mapper*"})
public class MybatisPlusSaasConfig {
    private static final Logger logger = LoggerFactory.getLogger(MybatisPlusSaasConfig.class);
    /**
     * tenant_id 字段名
     */
    private static final String TENANT_FIELD_NAME = "tenant_id";
    /**
     * 哪些表需要做多租户 表需要添加一个字段 tenant_id
     */
    private static final List<String> TENANT_TABLE = new ArrayList<String>();

    static {
        TENANT_TABLE.add("crm_customer"); // 客户表
        TENANT_TABLE.add("crm_project"); // 项目表
        TENANT_TABLE.add("crm_contact"); // 联系人表
        TENANT_TABLE.add("sys_employee"); // 员工表
        TENANT_TABLE.add("sys_depart"); // 部门表
        TENANT_TABLE.add("sys_role"); // 角色表
        TENANT_TABLE.add("sys_user_role"); // 用户角色关系表
        TENANT_TABLE.add("sys_position"); // 职务表
        TENANT_TABLE.add("sys_depart_role"); // 部门角色表
        TENANT_TABLE.add("sys_depart_role_user"); // 部门角色用户表
        TENANT_TABLE.add("sys_announcement"); // 系统通告表
        TENANT_TABLE.add("sys_announcement_send"); // 用户通告阅读标记表
        TENANT_TABLE.add("oms_product_sales_order"); // 成品销售订单表

        // 添加半成品销售订单模块相关表
        TENANT_TABLE.add("oms_semi_product_sales_order"); // 半成品销售订单表
        TENANT_TABLE.add("oms_semi_product_sales_order_detail"); // 半成品销售订单明细表
        TENANT_TABLE.add("oms_semi_product_sales_order_transaction"); // 半成品销售订单交易记录表
        TENANT_TABLE.add("oms_semi_product_sales_order_progress"); // 半成品销售订单进度表

        // 添加MES模块相关表
        TENANT_TABLE.add("mes_work_type"); // 工种类型定义表
        TENANT_TABLE.add("mes_employee_work_type"); // 员工工种关联表
        TENANT_TABLE.add("mes_piece_rate_standard"); // 计件工资标准表
        TENANT_TABLE.add("mes_production_plan"); // 生产计划主表
        TENANT_TABLE.add("mes_production_plan_detail"); // 生产计划明细表
        TENANT_TABLE.add("mes_production_plan_order_detail"); // 生产计划订单明细关联表
        TENANT_TABLE.add("mes_production_plan_task"); // 生产计划工种任务表
        TENANT_TABLE.add("mes_production_template"); // 生产计划模板
        TENANT_TABLE.add("mes_template_work_type"); // 生产计划模板工种配置
        TENANT_TABLE.add("mes_task_participant"); // 任务参与者表
        TENANT_TABLE.add("mes_employee_piece_wage"); // 员工计件工资记录表

        // 添加设计管理模块相关表
        TENANT_TABLE.add("oms_design_file"); // 设计文件表
        TENANT_TABLE.add("oms_design_file_audit"); // 设计文件审核记录表
        TENANT_TABLE.add("oms_design_file_process_log"); // 设计文件处理记录表
        TENANT_TABLE.add("oms_design_task"); // 设计任务表
        TENANT_TABLE.add("oms_design_task_progress"); // 设计任务进度表
        TENANT_TABLE.add("oms_inquiry_rule");
        TENANT_TABLE.add("oms_inquiry_rule_value");
        TENANT_TABLE.add("oms_inquiry");
        TENANT_TABLE.add("oms_inquiry_item");

        TENANT_TABLE.add("wms_warehouse");//仓库信息表
        TENANT_TABLE.add("wms_warehouse_location");
        TENANT_TABLE.add("wms_warehouse_location_bind");//商品库位绑定
        TENANT_TABLE.add("wms_material");//物料信息表
        TENANT_TABLE.add("wms_requisition");//领料单主表
        TENANT_TABLE.add("wms_requisition_profile");
        TENANT_TABLE.add("wms_requisition_accessory");
        TENANT_TABLE.add("wms_requisition_attachment");
        TENANT_TABLE.add("wms_requisition_detail");
        TENANT_TABLE.add("wms_supplier"); // 供应商表
        TENANT_TABLE.add("wms_supplier_contact"); // 供应商联系人表
        TENANT_TABLE.add("wms_supplier_material"); // 供应商物料关联表
        TENANT_TABLE.add("wms_purchase_order"); // 采购单主表
        TENANT_TABLE.add("wms_purchase_config"); // 采购配置表
        TENANT_TABLE.add("wms_material_warning_log"); // 物料预警日志表
        TENANT_TABLE.add("wms_material_detail"); // 物料明细表
        TENANT_TABLE.add("wms_inventory"); // 库存表
        TENANT_TABLE.add("wms_inventory_history"); // 库存历史表
        TENANT_TABLE.add("wms_inbound_order"); // 入库单主表
        TENANT_TABLE.add("wms_inbound_order_detail"); // 入库单明细表
        TENANT_TABLE.add("wms_outbound");//出库单主表
        TENANT_TABLE.add("wms_outbound_detail");//出库明细表
        TENANT_TABLE.add("wms_outbound_allocation_plan");//出库方案表
        TENANT_TABLE.add("wms_finished_goods_inbound");//成品入库单表
        TENANT_TABLE.add("wms_finished_goods_inbound_detail");//成品入库明细
        TENANT_TABLE.add("wms_finished_goods_inventory");//成品库存表
        TENANT_TABLE.add("wms_finished_goods_inventory_log");//成品库存变更表
        TENANT_TABLE.add("wms_finished_goods_outbound");//成品出库单表
        TENANT_TABLE.add("wms_finished_goods_outbound_detail");//成品出库明细


        // 添加生产计划模块相关表
        TENANT_TABLE.add("mes_work_type");
        TENANT_TABLE.add("mes_employee_work_type");
        TENANT_TABLE.add("mes_production_template"); // 生产计划模板表
        TENANT_TABLE.add("mes_production_plan"); // 生产计划主表
        TENANT_TABLE.add("mes_production_plan_detail"); // 生产计划明细表
        TENANT_TABLE.add("mes_production_plan_task"); // 生产计划任务表
        TENANT_TABLE.add("mes_production_plan_task_detail"); // 生产计划任务明细表
        TENANT_TABLE.add("mes_salary_calculation_record"); // 薪资计算记录表
        TENANT_TABLE.add("mes_task_participant"); // 任务参与者表
        TENANT_TABLE.add("mes_employee_piece_wage"); // 员工计件工资记录表
        TENANT_TABLE.add("mes_piece_rate_standard"); // 计件工资标准表
        TENANT_TABLE.add("mes_production_plan_order_detail"); // 生产计划订单明细关联表
    }

    /**
     * 判断当前用户是否为超级管理员
     *
     * @return 如果是超级管理员返回true，否则返回false
     */
    private boolean isAdminUser() {
        try {
            // 从TenantContext获取isAdmin标志
            Object isAdmin = TenantContext.get("isAdmin");
            if (isAdmin != null && Boolean.TRUE.equals(isAdmin)) {
                return true;
            }

            // 通过用户ID判断是否为超级管理员
            LoginUser loginUser = null;
            try {
                loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
            } catch (Exception securityException) {
                // 在异步线程中可能无法获取SecurityManager，返回false
                return false;
            }

            if (loginUser != null) {
                // 超级管理员ID
                String adminId = "e9ca23d68d884d4ebb19d07889727dae";
                return adminId.equals(loginUser.getId());
            }
        } catch (Exception e) {
            logger.warn("判断管理员状态失败", e);
        }
        return false;
    }

    /**
     * 判断当前请求是否需要跳过租户过滤
     *
     * @return 如果需要跳过租户过滤返回true，否则返回false
     */
    private boolean shouldSkipTenant() {
        try {
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return false;
            }

            HttpServletRequest request = ((ServletRequestAttributes) attributes).getRequest();
            if (request == null) {
                return false;
            }

            // 获取Spring MVC的处理器
            RequestMappingHandlerMapping mapping = SpringContextUtils.getBean(RequestMappingHandlerMapping.class);
            if (mapping == null) {
                return false;
            }

            try {
                // 获取当前请求的处理方法
                HandlerExecutionChain handlerChain = mapping.getHandler(request);
                if (handlerChain == null) {
                    return false;
                }
                Object handler = handlerChain.getHandler();
                if (handler instanceof HandlerMethod) {
                    HandlerMethod method = (HandlerMethod) handler;

                    // 检查方法上是否有@SkipTenant注解
                    SkipTenant skipTenant = method.getMethodAnnotation(SkipTenant.class);
                    if (skipTenant != null) {
                        logger.debug("方法 {} 跳过租户过滤: {}", method.getMethod().getName(), skipTenant.value());
                        return true;
                    }

                    // 检查类上是否有@SkipTenant注解
                    skipTenant = method.getBeanType().getAnnotation(SkipTenant.class);
                    if (skipTenant != null) {
                        logger.debug("类 {} 跳过租户过滤: {}", method.getBeanType().getName(), skipTenant.value());
                        return true;
                    }
                }
            } catch (Exception e) {
                logger.warn("获取当前请求处理方法失败", e);
            }
        } catch (Exception e) {
            logger.warn("判断是否跳过租户过滤失败", e);
        }
        return false;
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
            @Override
//            public Expression getTenantId() {
//                LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
//                String tenantId = "0";
//                if (loginUser == null) {
//                    tenantId = oConvertUtils.getString(TenantContext.getTenant(),"0");
//                } else {
//                    // 获取租户ID，如果为空或包含非数字字符，则使用默认值0
//                    String relTenantIds = loginUser.getRelTenantIds();
//                    if (oConvertUtils.isNotEmpty(relTenantIds)) {
//                        // 如果有多个租户ID（逗号分隔），取第一个
//                        if (relTenantIds.contains(",")) {
//                            tenantId = relTenantIds.split(",")[0];
//                        } else {
//                            tenantId = relTenantIds;
//                        }
//                    }
//
//                    // 确保tenantId是有效的数字
//                    try {
//                        Long.parseLong(tenantId);
//                    } catch (NumberFormatException e) {
//                        logger.warn("租户ID格式无效: " + tenantId + "，将使用默认值0");
//                        tenantId = "0";
//                    }
//                }
//                return new LongValue(tenantId);
//            }

            public Expression getTenantId() {
                LoginUser loginUser = null;
                try {
                    loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
                } catch (Exception e) {
                    // 在异步线程中可能无法获取SecurityManager，忽略异常
                }

                String tenantId = "0";
                if (loginUser == null) {
                    tenantId = TenantContext.getTenant();
                    if (tenantId == null) {
                        tenantId = "0";
                    }
                } else {
                    // 获取租户ID，如果为空或包含非数字字符，则使用默认值0
                    String relTenantIds = loginUser.getRelTenantIds();
                    if (oConvertUtils.isNotEmpty(relTenantIds)) {
                        // 如果有多个租户ID（逗号分隔），取第一个
                        if (relTenantIds.contains(",")) {
                            tenantId = relTenantIds.split(",")[0];
                        } else {
                            tenantId = relTenantIds;
                        }
                    }

                    // 确保tenantId是有效的数字
                    try {
                        Long.parseLong(tenantId);
                    } catch (NumberFormatException e) {
                        logger.warn("租户ID格式无效: " + tenantId + "，将使用默认值0");
                        tenantId = "0";
                    }
                }
                return new LongValue(tenantId);
            }

            @Override
            public String getTenantIdColumn() {
                return TENANT_FIELD_NAME;
            }

            // 返回 true 表示不走租户逻辑
            @Override
            public boolean ignoreTable(String tableName) {
                // 如果当前请求需要跳过租户过滤，直接返回true
                if (shouldSkipTenant()) {
                    return true;
                }

                // 如果是超级管理员，则所有表都不进行租户过滤
                if (isAdminUser()) {
                    return true;
                }

                return !TENANT_TABLE.contains(tableName);
            }
        }));
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        //update-begin-author:zyf date:20220425 for:【VUEN-606】注入动态表名适配拦截器解决多表名问题
        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());
        //update-end-author:zyf date:20220425 for:【VUEN-606】注入动态表名适配拦截器解决多表名问题
        return interceptor;
    }

    /**
     * 动态表名切换拦截器,用于适配vue2和vue3同一个表有多个的情况,如sys_role_index在vue3情况下表名为sys_role_index_v3
     *
     * @return
     */
    private DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
        dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> {
            //获取需要动态解析的表名
            String dynamicTableName = ThreadLocalDataHelper.get(CommonConstant.DYNAMIC_TABLE_NAME);
            //当dynamicTableName不为空时才走动态表名处理逻辑,否则返回原始表名
            if (ObjectUtil.isNotEmpty(dynamicTableName) && dynamicTableName.equals(tableName)) {
                // 获取前端传递的版本号标识
                Object version = ThreadLocalDataHelper.get(CommonConstant.VERSION);
                if (ObjectUtil.isNotEmpty(version)) {
                    //拼接表名规则(原始表名+下划线+前端传递的版本号)
                    return tableName + "_" + version;
                }
            }
            return tableName;
        });
        return dynamicTableNameInnerInterceptor;
    }

//    /**
//     * 下个版本会删除，现在为了避免缓存出现问题不得不配置
//     * @return
//     */
//    @Bean
//    public ConfigurationCustomizer configurationCustomizer() {
//        return configuration -> configuration.setUseDeprecatedExecutor(false);
//    }
//    /**
//     * mybatis-plus SQL执行效率插件【生产环境可以关闭】
//     */
//    @Bean
//    public PerformanceInterceptor performanceInterceptor() {
//        return new PerformanceInterceptor();
//    }

}
